﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LibMQTT.ProtocalIMPL
{
    using TCPServices;
    using System.Collections;

    public class MqPayloadVariable
    {
        internal List<byte> _AcceptCodes;
        /// 比特数组实际上包含2区和3区 需要一个索引来划分_AcceptCodes[]数组 
        internal Int32 PayPivotVal = -1;
        /// 左边为Mqtt固定字 右边为Payload有效载荷
        internal Int32 H1PivotVal = 5;
        /// 标记荷载关键字的在比特流中的索引值
        internal Int32 KeyLetterIndex = -1; 

        public MqPayloadVariable(List<byte> afterNSerial)
        {
            /* 需要注意 ToList<T>()函数 实际上会深复制一次内存 
             * 也就是说 实际上缓存拷贝的是一份影子 */
            _AcceptCodes = afterNSerial.ToList<byte>();

            // 移除5字节固定报文
            afterNSerial.RemoveRange(0, H1PivotVal);

            UnPackVariPayloadData(afterNSerial);
        }
        public override string ToString()
        {
            var s = new StringBuilder();
            s.AppendLine(string.Format("协议名称: {0}", ProtocalName));
            s.AppendLine(string.Format("协议等级: {0}", ProtocalLevel));
            s.AppendLine(string.Format("超时时间: {0}", KeepAlive));
            s.AppendLine(string.Format("标识位字: {0}", ConnInfo));
            return s.ToString();
        }

        /// 关键的两个数据容器 一个用于缓存 一个用于解码
        private Hashtable FlagResults = new Hashtable();
        protected string ProtocalName ;         // `MQTT`
        protected ushort ProtocalLevel;         // `4`
        protected ushort KeepAlive;             // 65535s [][]2字节最大65535秒
        protected FlagConnBag ConnInfo;// B[8] 8字节单字节转码协议标识符

        #region PrivateMethods &私有方法
        /* ------- ------- 解析报文2区 获取可变报文 ------- ------- */
        private void UnPackVariPayloadData(List<byte> dataSource)
        {
            // 获取协议名 2字节长度 + N字节协议名
            var MSBLSB = BitConverter.ToInt16(dataSource.Take(2).Reverse().ToArray(),0);
            // 计算关键载荷位的索引编号
            this.KeyLetterIndex = H1PivotVal + 2 + MSBLSB + 1;
            if (MSBLSB <= 0) throw new Exception("Mqtt报文2区首部不应该少于10字节");
            
            dataSource.RemoveRange(0, 2);
            ProtocalName = MqLotBase._BlockCoder.GetString(
                dataSource.ToArray(), 0, MSBLSB);

            dataSource.RemoveRange(0, MSBLSB);
            // 获取协议等级 1字节
            ProtocalLevel = Convert.ToByte(dataSource[0]);
            dataSource.RemoveAt(0);
            // 获取关键字标识位 1字节
            ConnInfo = new FlagConnBag(dataSource[0],KeyLetterIndex);
            dataSource.RemoveAt(0);
            // 获取会话周期 2字节 
            // 16个bit标识的单位s秒 最大不应该大于65535(2^16)秒 高低序为`高位优先`
            byte[] times = new byte[2];
            Buffer.BlockCopy(dataSource.ToArray(), 0, times, 0, 2);
            dataSource.RemoveRange(0, 2);
            KeepAlive = BitConverter.ToUInt16(times, 0);

            /* ------- ------- 截断二区在进行三区解析 Payload ------- ------- */
            /// Mqtt默认2区长度就是为10 协议名为`Mqtt`
            this.PayPivotVal = _AcceptCodes.Count() - dataSource.Count();

            var keys = new string[] { "ClientID", "WillTopic", "willMessage",
                "AttachedMsg","UserName" ,"PassWord"};
            int index = 0;
            // 将缓存数组中的数据 整体读到内存变量中 一部分写入HashTable
            while (index < keys.Length)
            {
                if (dataSource.Count == 0)
                    break;
                byte B1N = dataSource[0];
                int x = Convert.ToInt32(B1N);
                dataSource.RemoveAt(0);
                string _Payload = UCoder.GetString(dataSource.ToArray(), 0, x);
                dataSource.RemoveRange(0, x);
                this[keys[index++]] = _Payload;
                Console.WriteLine("载荷长度[{1}]:[{0}]/剩余长度 --->{2}", x, 
                    index, dataSource.Count);
            }
            Console.WriteLine(" --- 重写[]操作符 --- \n\t" + this["username"]);
#if !DEBUG
            FlagResults["UserName"] = string.Empty;
            FlagResults["PassWord"] = string.Empty;
            foreach(var key in FlagResults.Keys)
                Console.WriteLine("{0}:{1}", key,FlagResults[key]);
#endif
        }
        #endregion

        /// <summary>
        /// 内部类 - 连接标识位 内部解码结构体
        /// </summary>
        protected internal struct FlagConnBag
        {
            bool UserName; bool Password;
            bool WillRetain; bool WillQoS4; bool WillQoS3; bool WillFlag;
            bool SessionOK; bool SysReserved;

            internal Int32 _index;
            internal byte _BVal;
            public FlagConnBag(int VAL,int index)
            {
                _BVal = (byte)VAL;
                _index = index;
                // 在电子计算机中 &或 |于 操作总是成对出现的 
                UserName    = (VAL & (1 << 7)) !=0 ;
                Password    = (VAL & (1 << 6)) !=0 ;
                WillRetain  = (VAL & (1 << 5)) != 0; ;
                WillQoS4    = (VAL & (1 << 4)) != 0; ;
                WillQoS3    = (VAL & (1 << 3)) != 0; ;
                WillFlag    = (VAL & (1 << 2)) != 0; ;
                SessionOK   = (VAL & (1 << 1)) != 0; ;
                SysReserved = (VAL & (1 << 0)) != 0; ;
            }
            public override string ToString()
            {
                var s = string.Empty;
                s += Environment.NewLine;
                s += string.Format(" ---- 荷载关键字[{0}`{1:X2}`] ---- \n",_index, _BVal);
                s += string.Format("是否包含用户名:{0}\n", UserName);
                s += string.Format("是否包含密码:{0}\n", Password);
                s += string.Format("遗嘱保留位Retain :{0}\n", WillRetain);
                s += string.Format("遗嘱Qos4位  : {0}\n", WillQoS4);
                s += string.Format("遗嘱Qos3位  : {0}\n", WillQoS3);
                s += string.Format("是否有遗嘱  : {0}\n", WillFlag);
                s += string.Format("是否清除会话 : {0}\n", SessionOK);
                s += string.Format("Mqtt保留字 : {0}\n", SysReserved);
                s += " ---- MQTT V.31 ---- ----  \n";
                return s;
            }
        }
        /// 字符集
        internal static Encoding UCoder { get { return MqLotBase._BlockCoder; } }
        /// 重写操作符`[]`
        public string this[string PayloadKey]
        {
            get
            {
                if (FlagResults.ContainsKey(PayloadKey))
                    return FlagResults[PayloadKey].ToString();
                return
                    string.Format("传输过来的报文中有效载荷不存在关键字+[{0}]", PayloadKey);
            }
            set { FlagResults[PayloadKey] = value; }
        }
        /// <summary>
        /// 查看箱式比特流 
        /// </summary>
        /// <param name="LineCounts">每一行多少个元素</param>
        /// <returns>将比特流数组拼接为一个完整的字符串 并划断123区</returns>
        public string SeeBytesMatrix(int LineCounts)
        {
            StringBuilder sBuffer = new StringBuilder();
            for(int i = 0; i < H1PivotVal; i++)
                sBuffer.Append(string.Format("[{0:X2}].",_AcceptCodes[i]));
            sBuffer.AppendLine("\n -------------- 划断12区 -----------------  ");

            for(int i = H1PivotVal; i < PayPivotVal; i++)
                sBuffer.Append(string.Format("[{0:X2}].", _AcceptCodes[i]));
            sBuffer.AppendLine("\n -------------- 划断23区 -----------------  ");

            int index = 0;
            for(int i = PayPivotVal; i < _AcceptCodes.Count; i++)
            {
                sBuffer.Append(string.Format("[{0:X2}].", _AcceptCodes[i]));
                if (++index % LineCounts == 0)
                    sBuffer.AppendLine();
            }
            /// Console.WriteLine("aa --->{0} Length(Array)--->{1}",aa,_AcceptCodes.Count);
            return sBuffer.ToString();
        }
    }
}
